Apr 11 2025
SoftwareBackend

Hey everyone! It’s been a while. I’ve had a pretty good reason for my absence — I’ve swapped waking up from my team’s nightly PagerDuty alarms for waking up from something far more delightful: my beautiful 3-month-old baby girl.

The comparison between a crying baby and a PagerDuty alert isn’t coincidental: In both cases, I go through the same emotional rollercoaster: denial, bargaining, and acceptance — all within a matter of minutes. With PagerDuty, it goes something like: “Maybe it’s a false alarm?”, “Maybe the error only happened once and will magically resolve if I click ‘acknowledge’?”, “Oh crap, I guess I really have to get up.” With the baby: “Did I hear that right?”, “Maybe if I wait a second she’ll settle back to sleep?”, “Oh crap, I really have to get up.” (Literally... crap, in the crying-baby example)

Over the past year, I’ve worked hard to improve our on-call experience — making alerts more accurate so no one loses sleep over false alarms. Along the way, I’ve learned a lot about what’s truly worth monitoring. So here it is: what you should be tracking in your app to make sure you’re only waking up for the things that really matter.

What Does Good Monitoring Look Like?

If you’re waking up in the middle of the night, it better be for a damn good reason. You want to be alerted only when something serious happens — not for minor bugs or things that can wait until morning. However, you want to know as soon as possible when things go bad. Ideally, you’d be alerted before your users notice anything wrong.

The good news? That’s totally doable, if you focus on the right metrics and make sure every alert includes enough context so anyone on-call can quickly take action — even when half-asleep.

The bad news? Setting up the right alerts is not a one-and-done task. It’s an ongoing effort. You’ll need to keep adjusting thresholds, adding and removing alerts, and continuously refining your monitoring setup — just like maintaining your codebase or sharing knowledge across the team.

Key Metrics to Monitor

There are tons of metrics you could monitor — but let’s focus on the ones that really move the needle. We’ll start with low-level system metrics and work our way up to high-level user behavior.

System Metrics (CPU, Disk Usage, etc.)

Before diving into application-specific monitoring, you need to ensure the underlying system is healthy.

Metrics like high CPU usage, memory pressure, or low disk space can lead to unexpected behavior, crashes, or even data loss.

Set up alerts for these so you can scale up resources or apply hotfixes before users feel the impact. If auto-scaling is an option — and it fits within your cost constraints — even better. (Think AWS SQS queue size, DB storage, etc.) That way, you’re only alerted when manual intervention is really needed.

These metrics are usually collected via a monitoring agent on the machine, like node_exporter with Prometheus.

Service health (AKA “Liveness”)

This one’s non-negotiable. You need to know whether your service is actually up.

This is typically done by exposing a /live or /health endpoint and polling it regularly. Tools like UptimeRobot, BetterUptime, and Site24x7 can handle the health monitoring for you.

If your services are running on Kubernetes, you probably know the liveness configuration. When using livenessProbe, Kubernetes periodically checks if your service is up (by sending an HTTP request, checking if a port is open, or executing a command), and even restarts it if the probe fails a few times, making it proactive.

livenessProbe:
	httpGet:
		path: /health
		port: 8080

Application Performance (APM)

Application Performance Monitoring is a set of tools that help to track the performance and availability of applications.

APM is a big deal — and for a really good reason, It helps you understand how your app is performing in real-time. Here are the key APM metrics you should alert on:

  • Apdex

    Application Performance Index, is a metric that measures user satisfaction with our app’s performance. It’s a score from 0 to 1, where 0 is total frustration and 1 is total satisfaction. It’s user-friendly and easy to understand. The computation is based on a threshold (T), and the number of requests in a period where the response time was ≤ T (This is a simplified explanation). Most APM tools (Datadog, New Relic, Grafana) support this out of the box. Define a reasonable threshold — and alert when things drop.

  • High Error Rate (5xx, usually):
    Spikes in error rates often signal a problem. Most monitoring tools support anomaly detection for these. In Prometheus, for example, you can use
    increase(..{status="5.."}) to identify trends.

    4xx errors are less commonly monitored, but may be useful for spotting abnormal client behavior.

  • Key Request / Key Transaction
    Some endpoints are business-critical — like
    /login or /payment. Many APMs let you monitor these separately, with their own thresholds and alerts, so they never slip through the cracks.

User Behavior (Synthetics Canaries / Production tests)

Synthetic (or active) monitoring simulates full user flows in production, by running “Canaries” - automated scripts that mimic user actions by calling API requests and validating their response, etc. You can think of synthetics as running a bit of your CI suite continuously, in production.

Use it for high-value flows only, since it's more resource-intensive. Synthetic checks can catch issues before users even notice — making them a powerful proactive tool.

You can utilize Synthetic Monitoring in AWS (CloudWatch), Datadog, New Relic, and more.

Bonus: Frontend / Web Monitoring

Frontend monitoring is its own beast. Why? Because the frontend runs on the user's device — not on your infrastructure. That means you have to explicitly send logs back to your servers.

Thankfully, most APM tools offer client-side plugins to automatically capture:

  • JavaScript errors
  • Network errors
  • Web performance stats
  • And more

Still, frontend issues can be tricky to detect because they often boil down to user experience. (“I clicked the button and nothing happened.”)

Here’s what you should track:

Web Vitals

Web Vitals (by Google) aim to quantify user experience — things like UI responsiveness and visual stability.

Some key metrics to know:

  • LCP (Largest Contentful Paint) — how quickly the main content loads
  • CLS (Cumulative Layout Shift) — how much the layout jumps around

Most frontend monitoring tools support Web Vitals, or at least a subset. More info here: web.dev/vitals

Error Rate

Track trends in JavaScript errors — things like TypeError, NetworkError, etc. This can help you catch bugs before users report them.

Heads up: You might get duplicate alerts here if a client-side error reflects an existing backend issue.

Browser Canaries

Some monitoring tools like AWS CloudWatch, let you define a Canary with access to a headless Google Chrome Browser. This way, we can simulate real user actions in the browser (think Selenium or Puppeteer).

They’re great for validating key journeys (like login or checkout) end to end — and catching issues before users hit them.

Summary

We covered the most important metrics to monitor in your app so that your alerts are meaningful, actionable, and worth waking up for.

It’s not all or nothing — even starting with a few of these will improve your setup dramatically.

Just remember: monitoring is never done. Keep tweaking those thresholds, refining alerts, and removing the noisy ones. Your future self (and your sleep schedule) will thank you — and so will your users.

Sleep tight,

Omer

Dec 14 2024
Soft Skills

A few weeks ago, a manager in the office approached me for advice. "How can I avoid micromanaging my direct reports?" they asked. "During your team meetings, you seem to give your workers considerable freedom and space—you don't tell them what to do, but rather ask guiding questions."

I was surprised by this observation since I don't consider myself a hands-off manager. In fact, I insist on knowing every detail, conducting code reviews, and closely monitoring any delayed tasks.

After reflection, I realized that my lack of micromanagement is something my employees earn—not a birthright. They must work hard to achieve this trust, and not everyone receives the same level of close supervision.

This realization inspired me to write this article: to highlight how you, as workers, can significantly influence whether your manager micromanages you.

The Key: Get Into Your Manager's Head

If you're frustrated with micromanagement, you need to understand its root cause: Why is your manager involved in every detail and constantly asking about progress? The answer lies in understanding your manager's perspective and motivations.

But first, a disclaimer.

Let's be clear - some managers will micromanage regardless of what you do. This article isn't about those managers. Instead, we'll focus on generally competent managers who vary their management style: sometimes giving you space, other times hovering like a buzzing bee. Maybe they're hands-off with others but watch your work closely.

Let's explore why this happens and how to handle it.

But First, What Do Managers Want?

Generally speaking, your manager excelled in their previous role—that's how they got promoted. In software development, this usually means they were outstanding individual contributors who consistently delivered high-quality features on time.

After promotion, however, they face a paradox: they have more influence but less direct control over project pace and quality of execution. This shift often leads to frustration.

Instead of making their own estimates, they have to take your word for it. Rather than having firsthand knowledge as a lead developer, they have to retrieve that information directly from you—constantly and repeatedly.

Moreover, your manager faces significant pressure from upper management about project progress, deadlines, and business risks. Like everyone else, they're being evaluated and want to excel to advance their career.

These factors combine to create the "control freak" micromanager you see today. Micromanagement is essentially their attempt to gain some control and predictability over their chaotic work-life.

So what can you do?

Earn Their Trust

The logic is simple: the more your manager trusts you, the more control they feel over delegated responsibilities, and the less they'll micromanage you.

Building your manager's trust is a gradual process. You'll earn it step by step, accumulating small wins until they fully trust your judgment.

Start with the basics: complete tasks within estimated timeframes, deliver bug-free features, and promptly flag any roadblocks that need their intervention. With each success, trust grows—until your manager knows instinctively that tasks in your hands will be well-handled.

Beyond excellent performance, focus on improving your upward communication. Here's how to effectively share information with your manager:

Change Your "Verbosity" from "CRITICAL" to "WARN" (Sometimes Even "INFO"!)

Most of us only report major issues to our managers—like missed deadlines or production problems—as if we're set to "CRITICAL" logging level.

Instead, switch to "WARN" level and regularly share updates about less urgent matters. Don't wait for scheduled meetings. This proactive communication keeps your manager informed and prevents surprises. It enables them to adjust plans early when needed. Remember: predictability is key!

Share updates like "Waiting on DevOps support before proceeding," "Testing is more complex than anticipated—need an extra 2-3 days," or "Feature is in review, expected completion by end of day" with your manager as soon as they happen.

Remember: "You update me too much on your progress" - Said no manager, ever.

Be Proactive in Your 1:1s, Use Them Wisely

While regular 1:1s with your manager are valuable, they often feel more like interrogations than conversations.

These meetings typically start with casual "How was your week?" before shifting to project status updates and questions about delays. This questioning pattern emerges because managers need information to report upward.

If this sounds familiar, you're probably not being proactive enough in your 1:1s. Your manager has learned to ask about every detail because they aren't getting updates any other way!

Take control of these conversations. Plan and prepare in advance key points about your projects and what you need from your manager to succeed.

Engage actively by asking questions and highlighting concerns. You'll find it more empowering to proactively report progress rather than respond to queries. This approach makes meetings more productive—and enjoyable—for everyone.

In Conclusion

This article empowers you, the developers, to actively manage your relationship with your manager while understanding their perspective.

As you build trust, you'll notice your manager giving you more autonomy and space to work independently.

Sep 07 2024
Soft Skills

Talking in front of an audience is not natural for me. Back in school, when I wanted to participate in class, I remember my face turning red just from thinking about it, my heart beating fast, and in most cases, I eventually didn’t even participate. This is a familiar story for every shy child.

However, I refused to let the introvert in me determine my life’s course, and occasionally put myself in situations where I had to speak in front of people. I became an officer during my military service, presented in my workplace many times, and a few months ago, I even lectured, for the first time in my life, in a very big tech conference in Tel-Aviv.

I have come a very long way: workshops, courses, and practices, and the good news is: you can get so much better at public speaking if you want to.

I do have an unfair advantage - I ❤️ Building Presentation Slides

This odd passion of mine goes way back, to when I was 7 years old (yep, this is not a mistake). It was at that age when, my mother taught me how to use Microsoft Office software - she was a teacher, learned how to it them at this time, and this was her way of practicing. So, I got obsessed with PowerPoint and started to build presentations on any topic in the world. My masterpiece was, what I called, “the 100 slides Presentation”, which was an experimental art mix of endless animations and sounds, like only Office 98 could provide. SO much better than Barbies!

I understand that not everyone likes to create slides like I do, but it’s necessary to embrace the importance of a good set of presentation slides.

A good slide is your best friend when presenting, especially if you’re still not giving speeches in your sleep: it keeps you and the audience focused, reminds you what you wanted to talk about, and is appealing and funny, yet informative at the same time. It gives you confidence. Therefore I believe it’s crucial to discuss building the correct presentation.

However, consider well when it’s required to make presentation slides. If it’s an informal meeting, or if the meeting is more a discussion than tranferring knowledge, then you can skip the slides-making part.

Tips for every part of the way

The rest of this article is kind of a “cheat sheet” for helping you give a good presentation, from tips I gathered in all workshops and courses I’ve been through the past years.

I divided the presentation-making plan into 2 parts: “Before” and “During”. “Before” is the part where you consolidate and build your presentation’s content. The “During” is all the things you should consider while you are presenting your content.

Before

Before you start working on the content

“What’s in it for me” - Very famous concept in public speaking. You should consider the 1-3 concepts you want your audience to take from your lecture. In other words, what are the audience takeaways? how will your lecture help them? Why should they listen to you?

Presentation structure

  1. Strong beginning: a “hook” is very important in slides, it can include a personal story, it can be something funny like a joke or an anecdote, and it can be anything else you find captivating. Its target is to attract the audience’s attention right at the beginning, like bait.
  1. Discuss the problem (AKA “The Motivation”) before you talk about the solution
  1. Every few slides, summarize where are we standing & what have we learned so far. Generally, assume that your audience has an issue with concentrating (because they do. we all do).
  1. Last slides: Repeat the “what’s in it for me” and mention how we achieved it in the lecture.
  1. Live demos / Live coding are stressful and tend to fail in real time. Use a recording where possible.

Slides design

  1. One concept per slide - No bullets, if possible. I know, you are used to writing a long list of bullets. But they are distracting and not helping you or the audience. A picture and a huge title are better. Or, if you must, a 1-2 word bullets that you show one at a time. I love using icons as bullets as demonstrated below.
  1. The slides’ design should be consistent and not distracting
  1. Highlights keep the audience focused. If there’s a lot of data in one slide (graph, architecture, screen recording), highlight only what matters.
  1. Show a “progress bar”: Show the agenda of the presentation at the beginning and during the lecture. Help people get back on the train in case they missed a few sentences.
  1. Code: if applicable, show it as huge as possible, with highlights on every line you present. Use Syntax highlighting.
  1. Displaying data: Think of one significant number you can show instead of a chart. Charts are not as effective in slides.

Example for “One concept per slide”

Example for “Bullets with no bullets”

Audience Engagement

This part can be either in the “Before” or “During”, but I decided to include it here as the planning of the engagement happens before the lecture starts, in the planning phase.

  1. A gauge of hands: This is a good tool to know your audience and to keep them engaged during the presentation. “How many people here already used X?” etc. Don’t push it though, 1-2 gauge is enough.
  1. Action items: Give the audience action items they can do afterward. This will increase the chance they will take something from the lecture.
  1. Summarize and send later: During the lecture, the audience shouldn’t be focused on memorizing / writing, because they won’t listen. You can create a QR code that leads to your slides in a drive so people can scan it and get to your content.

Example for action items + QR

During

A few minutes before your lecture starts

  1. Go warm yourself up, especially if you sat all day (which is common if you’re in tech). Bounce and stretch like before an exercise. You can’t expect to be energized without this! Also, it helps a lot with the pressure.
  1. Memorize/rehearse the first few lines of the presentation by heart. It will help get you into the flow of the lecture more easily.

When you are on stage, one second before starting to talk

This is the most nerve-wracking moment, so this part is more like a meditation than actual tips and is surprisingly similar to handling a panic attack.

  1. Think about what you physically feel right now. do you feel your heartbeat? Are you sweaty? are you trembling? Understand that this is welcome and good. Adrenaline gives you this extra boost of energy and improves your performance overall.
  1. Relax your shoulders. Relax the tension in your eyes. Acknowledge your feet. Feel your breath.
  1. Repeat the few lines you memorized in your head.

Body language - during the lecture

  1. Show enthusiasm! If you do this, everything else is pretty much forgivable
  1. Pause after an important statement. It gives the audience a second to absorb, and for you to breathe. This is subtle, don’t pause for too long or it will become weird.
  1. Look at the faces in the crowd, especially of people that make you feel confident and relaxed.
  1. Smile!

This is me! do you feel the enthusiasm?

General tips

  1. Practice makes perfect. if in doubt, practice your lecture again until you’re confident enough that you know all the material perfectly.
  1. Something I once heard in a lecture and liked: “Talking to an audience is like covering with a thick blanket at night. At first, it’s cold and you warm the blanket more than it warms you, but eventually, you get heat back from it”. This is the love and appreciation you get from the audience after you teach them something new or inspiring.

Summary

I hope you gathered some good tips that help you pass your next presentation easily and comfortably. At first, when you try to apply them, it’s going to feel like when you first learned how to drive: “How am I supposed to look at the road, use the turn signals, shift a gear, and change a lane safely all at the same time?!”. But as you do it over and over again, it starts to become natural for you.

I’m sure I also have a lot more to learn myself on this topic, I’m not a TED speaker or anything. But I believe this was a good point to pass some of my knowledge to those who need it.

Feel free to consult with me always, and good luck to you with your next presentations!

Apr 25 2024
SoftwareArchitectureBackendFrontend

Everybody looooves dashboards.

In the world of Big Data, dashboards help us grasp complex information in seconds, making them popular among both customers and management, and a must-have in every business web app. However, planning such a dashboard system isn't simple; its architecture involves performant backend endpoints, data-engineering superpowers, and beautiful frontend charts.
But don’t worry—I've created this '101' guide so you can skip directly to the building phase!
Mar 23 2024
Backend

The cult movie "The Princess Bride” besides being one of my favorite movies of all time, is a movie that is full of iconic quotes. The most famous one is, "My name is Inigo Montoya. You killed my father. Prepare to die." But one of my favorites is this one: After Vizzini keeps commenting "Inconceivable!" on everything, Inigo tells him: "You keep using that word. I don’t think it means what you think it means".

I believe we all do it once in a while. Throwing a term into a conversation, a term we think we understand because we heard it many times before, and just move on with our lives. Sometimes we even feel good about ourselves that we managed to use that term in a conversation. But how often do we stop and ask ourselves if we understand what those terms mean?

Let’s use technical terms the right way

The first reason to use technical terms the right way is to maintain our reputation as professional individual contributors.

In addition, I believe that a thorough understanding of everything you do will also help you do your work better. Intuitive, but still worth mentioning.

1. Async (Specifically in JS and Python)

This is a very common mistake. It’s especially confusing because async-await is implemented differently in each programming language. Let’s see how to use it properly.

How people sometimes refer to it: Parallelism / Multi-processing / Multi-threading

How to not use it in a sentence: “We moved our Python microservices to be based on async-await, so it can run multiple threads and do the work faster”.

What it really means: In single-threaded languages, like JS and Python*, async is used so we won’t block the execution of our program until long I/O-bound tasks finish, for example: sending HTTP request, reading from a file or DB, and more. Moreover, if our program needs to wait for CPU-bound tasks, async-await will not be the right choice and will cause our program to run slower, because it’s still one thread, so we won’t be saving any time.

It’s a bit counterintuitive, but waiting for I/O-bound tasks does not require our program to run on multiple threads, and of course, not on multiple CPU cores. Instead, those languages manage an event loop, and multi-tasking between the events, while utilizing the OS to run the I/O-bound tasks in parallel.

I love this example of making a lazy dinner, to understand the difference between the different terms:

  • Synchronous: I will order a pizza. Will sit in my chair until the pizza is delivered. Then will make a salad and open a bottle of wine (cause I’m a classy girl).
  • Multi-threading: I’ll make a pizza but once in a few seconds I’ll change to making a salad and open a bottle of wine, get back to the pizza making, etc, until everything is done.
  • Multi-processing: My friend will bake a pizza while I’ll make a salad and open a bottle of wine.
  • Async: I will order a Pizza. While the Pizza is delivered, I will make a salad and open a bottle of wine.

Be smart, order the pizza when possible 😉.

* Technically Python has threads, but the GIL makes sure only one thread can run on each process every time.

2. REST

How people sometimes refer to it: HTTP API

How to not use it in a sentence: “In my last role I created a RESTful API for managing trees. For example POST trees/123/addNodeToTree {id: “456”}” / “Let’s just add a REST API for that GraphQL endpoint”.

What it really means: As I also wrote here, REST (Representational state transfer🤢) is a set of rules and constraints for designing an API. It’s not a protocol per se, but more of a rule of thumb. It can utilize every network protocol, although it’s most common to be used with HTTP.

The idea behind REST is to manage resources. A resource is everything the user is handling (creating, watching, editing, removing). For example in our shows API, a TV show is an asset, but also an actor can be an asset, a producer, a movie, etc.

The real REST specification is so strict and extreme, so it’s fair to assume that most of the HTTP APIs you worked on, that you thought of as REST, aren’t that at all.

3. Race Condition

How people sometimes refer to it: Unexpected result on each execution

How to not use it in a sentence: “This is a really weird UI bug, it sometimes happens and sometimes not. Probably a race condition.”.

What it really means: Race condition is when multiple threads modify the same resource at the same time and cause unexpected behavior. For example, in the lazy dinner example, if my friend puts the pizza in the oven but at the same time I will turn the oven off, we won’t have anything to eat.

For the same reason mentioned above, you can put this option off the table if you are using JS. Try to understand the real root cause behind the issue, it can be different network request times for each execution, and a lot of other stuff, but it’s not a race condition.

4. AI

Oh man, don’t even get me started.

Knowledge is power, use it well

I’m sure that now you will constantly hear other people misuse those terms I mentioned above, and you may get an urgent need to patronize them with your knowledge. Use this knowledge for the good and help everyone around you be better at what they do. Empathy is key.

And of course, if you have ideas for other such misusages, write them here in the comments! ⬇️

Mar 23 2024
SoftwareUX/UI
Have you always dreamed of designing your own features and bringing your ideas and inspiration to life?
As developers, we have a huge advantage when it comes to using Figma for UI design.
We are already used to thinking in terms of component reuse, robustness, and coupling.

In this session, you will learn how to harness some of the principles we know from SOLID and Clean code to create beautiful UI components in the most popular design app in the high-tech industry today.

(My talk from the annual Reversim Summit, taking place in Tel Aviv. The lecture is held in Hebrew)

Mar 1 2024
Productivity

My team like to say I have 9 days a week because I sometimes get so much work done. Well, they don’t always say that. Sometimes they say I have a secret twin sister doing missions for me. You get the picture…

While I don’t have any of those, I have some nice productivity tricks I gathered in years of working half-time during university, and after I switched to managing. These experiences forced me to gain healthy work habits that help me until today.

In this article, I’ll share all of those tips with you (Well, not all. Gotta keep some of the secret ingredients!).

🧠 1. Maintain a “Second Brain”

The sooner you understand that your memory and mental capacity are limited, the better. Think of a “second brain” as an up-to-date snapshot of your status at work.

I got used to this method while working part-time during university. I only worked 2 days a week, so the next week when I came to work I had to remember in exactly which point I stopped (It’s a bit like a Context Switch, isn’t it?) to make these 2 days really efficient.

How-to:

To maintain a second brain, Choose a comfortable Notes/Notebook app (I love Notion) and make it your source of truth for everything you do at work. For example, it could contain-

  • The things you’d like to get done today
  • A page for every feature you’re working on, with your progress status and open questions
  • Things you want to say on 1:1s and don’t want to forget
  • Cheat sheets (like, shell commands you like)
  • If you’re a manager, a page for each employee + a summary of your 1:1s

And More!

What I love about the second brain, is that as soon as things are written down, you don’t need to memorize anything, so this habit has a great calming effect. Everything is under control. You got this.

🚥 2. Do the most important thing every time

Working efficiently, especially in management, is understanding that there are some things you don’t like to do, and these things naturally get pushed by you because you determine your schedule. It’s dangerous because sometimes these are very important things for the company or other people.

These things are different for each one: some don’t like to write yearly reviews, the other hates building presentations, and the other hates handling performance issues, and would rather code new features all day, instead.

So this part is all about constantly prioritizing tasks according to what really matters. It takes some practice but gets easier as it goes.

How-to:

In your notes app, maintain 3 to-do lists:

  • Urgent (e.g check why the automation fails)
  • Medium (e.g separate epic to stories, finish a task)
  • Nice to have (e.g document something, think about how to refactor something, remind someone to give you an answer)

Every time a new task arrives, whether from Slack, or you just had a cool idea, or your boss asked you - unless it’s 2 minutes of work, put it in one of those lists.

When you start your day, you know exactly what to do. Whatever is at the top! If you are blocked by someone, waiting, or just have some spare time, you can take tasks from the lower priority list. This way you’ll always have something to do, and eventually you’ll get to anything, in the right order.

Like any new habit, it takes time to get used to it and write down every task. But when your list is well maintained, this is when your time is best used.

🗨️ 3. Manage your Slack, don’t let it manage you

Every instant messaging app is a bit different, but I’ll talk here about Slack, as it’s one of the most common ones in workplaces, and it’s the tool I’m working with.

The fact that communicating in Slack is so slick and easy, also makes it a walking disorder of attention due to the endless amount of messages, mentions, and notifications.

How-to:

Do you start your day by reading all Slack messages? Don’t.

Slacking never ends, and you might find yourself still answering messages half a day, not getting anything done, especially if you’re collaborating with a lot of people which happens a lot in management and customer-oriented positions.

Instead-

  • Start the day with a quick win, something to feel productive about, 30 minutes up to one hour. Review someone’s code. Close a 1sp task. Answer the company poll. It doesn’t matter what, as long as 1 hour is enough. Get your dopamine working!
  • Prioritize Slack messages. When you already are answering, Go by this order: DM → Channels → Replies you’re Mentioned in (A bit robotic, but works). People who DMed you are the ones that need you most urgently. Channels are the ones that are the most easy to finish reading, as the messages there usually don’t refer to you directly. Replies you are mentioned in are the most complex to handle, as you will need to read a whole thread until you understand what’s required from you, so you want to give this your full attention. This takes the highest amount of time.
  • Mark as unread. If you read a message and can’t handle it within a minute, “Mark as unread” is your best friend. Some people also love to use the “Save for Later”, but I’m not a fan, as I tend to not get back to those “Later”s if they’re not in my Notion.

Well, easier said than done…

It’s not completely fair, what I wrote here, as I suggest you adopt at once habits that it took me years to develop.

However, I hope that you do find value in at least one of these tips and that they’ll help you be more productive and in control. If you need to implement a new habit, I recommend reading the wonderful Atomic Habits (I also wrote about it here), which provides down-to-earth methods for embedding new habits into your daily routine.

If you have any other work-life hacks, tips, or tricks, please let me know!

Feb 3 2024
SoftwareFrontend

Have you ever encountered the need to introduce your app’s UI to your users?

In this article, we will learn how to build our own “Product Tour” in React JS, with maximum customization and minimum effort and cost, using tools we all “have at home” (I used Zustand for state management and MUI for UI components).

The problem

Suppose you have an application with numerous features and abilities, and your application’s new users often need guidance on how to use it. Alternatively, suppose you launch a new UI version of your app, and you want to walk your users through the new changes in the system.

Ideally, we would personally walk every one of our customers through our app, and guide them until they understand every aspect of it. However, in the real world, we don’t always know all of our customers, and we don’t have the time or the will to guide them all. Luckily, we have product tours to help us!

What are product tours?

“Product Tour”, sometimes called “Product Walkthrough”, is a tool for showcasing an app UI, usually a web app, by interactively highlighting different parts of the app, one part at a time, resulting in an interactive in-product guide. Each such “highlight” familiarizes the user with the UI part, and encourages them to engage with it.

Ok, I’m on board! Now, how do I get such a product tour?

If you want to embed a product tour into your app, you have 3 options:

1. Use a “Digital Adoption Category” off-the-shelf product

There are multiple great tools that will give you excellent product tours within minutes. For example: Walkme, Pendo, Appcues, and much more. The downside for using such a product is that it costs, and you probably already are paying for endless other SaaS apps, so think well if you are willing to pay just to have a product tour. If you plan to only embed one guide, for instance, it probably won’t be worth the effort.

2. Use a Tour library

At least in React JS, there are 2 dominant tour libraries: reactour and react-joyride. They both have a pretty similar interface: They both define a list of steps, where each step specifies the CSS selector of the element it needs to show next to.

For example, this is the interface of react-joyride: (taken from their docs)

import Joyride from 'react-joyride';

export class App extends React.Component {
  state = {
    steps: [
      {
        target: '.my-first-step',
        content: 'This is my awesome feature!',
      },
      {
        target: '.my-other-step',
        content: 'This another awesome feature!',
      },
      ...
    ]
  };

  render () {
    const { steps } = this.state;

    return (
      <div className="app">
        <Joyride
          steps={steps}
          ...
        />
        ...
      </div>
    );
  }
}

Although both of the libraries are open source and thus free, they have other underlying issues. The first one is the size of the packages, which goes from 123 kB in reacttour to 452 kB in react-joyride, which is a lot for something that small (no offense), making our app load slower due to the bigger bundle size.

Additionally, there are customization issues. Although both of the libraries allow you to have your custom Tooltip components, you will generally need to work harder to adjust the components to the required interface. In react-joyride it becomes even more complicated if you want to customize the current step logic because then the tour becomes “controlled” so you need to maintain an inner state, so you end up working hard for the library instead for it to work hard for you!

Regardless, I find relying on CSS selectors can be risky and flakey. CSS properties tend to change over time, and we don’t want our guide to ever break.

3. Do it yourself

If you want to have maximum customization in your product tour, without spending time and money, this may be the perfect solution for you.

Our DIY solution

Our solution is inspired by the interfaces of the 2 libraries mentioned above, with one significant difference: We don’t rely on CSS Selectors. Instead, each step will have its indicative and unique name. We will shortly show how we understand when to show each step and where.

But first, a disclaimer: on the spectrum between a "Enterprise-grade product" and a “quick and dirty” solution, this solution is in the “quick and dirty” department. It suits best when you need a single interactive guide, custom-tailored to your app components and libraries. Therefore, if product tours are going to be used regularly in your app, in different scenarios, consider purchasing a product that covers everything for you.

We will use an example of a calorie tracking app and implement our Tour inside. (You might find some familiar Beatles songs from the Magical Mystery Tour album, one of their best!)

Building our Product Tour

Before we start, make sure you have-

  • A React JS app
  • A Popover component. Every component library has one. In my example I used Material UI’s Popover, which is based on Popper.js.
  • A state management solution. I used Zustand because it’s very easy to implement and use, but you can also use React context with React state, or Redux (if you really must 😈).

We have 3 steps for implementing our tour:

  1. Define tour state and stops, and expose the tour store, a hook that will manage the tour state.
  1. Create the tour building block: the <TourStop/> component
  1. Adding the tour stops to our app.

Define tour state

first, we will define our stops.

const stops = [
  {
    id: "side-menu",
    content: "Click this button to see other food categories",
  },
  {
    id: "login",
    content: "Click here to login and see your preferences",
  },
  {
    id: "protein",
    content: "Here you can see the amount of protein in each food",
  },
];

id is an indicative name for the stop. content is a string that contains the stop text, it can easily be modified to be a ReactNode if we want to show more complex objects.

Now that we have the stops, we can add our store. Zustand makes it easy for us to expose a hook for managing complex state.

import { create } from "zustand";

export const useTourStore = create((set, get) => ({
  isShown: true,
  index: 0,
  isFirst: () => get().index === 0,
  isLast: () => get().index === stops.length - 1,
  prev: () =>
    set((state) => ({
      index: Math.max(0, state.index - 1),
    })),
  next: () =>
    set((state) => ({
      isShown: state.index < stops.length - 1,
      index: Math.min(state.index + 1, stops.length - 1),
    })),
  skip: () => set({ isShown: false }),
  getCurrent: () => stops[get().index],
  isStepActive: (stepId) => get().isShown && stepId === stops[get().index].id,
}));

Our store is exposing a few properties and setters-

  • isShown: Whether the guide is active now
  • index: The index of the current stop
  • isFirst(): Whether the current stop is the first one
  • isLast(): Whether the current stop is the last one
  • prev(): Go to the previous stop, if possible
  • next(): Go to the next stop, and hide the guide if it was the last stop
  • skip(): Hide the guide
  • getCurrent(): Get the current stop object (id and content)
  • isStepActive(stepId): Get whether the step is the one that is currently shown on the screen

Our store can easily be consumed like this:

import { useTourStore } from "./tourStore";
const { getCurrent, next, skip, prev, isLast, isFirst, isStepActive } =
    useTourStore();

Now that we have a way to control our tour stops, we’ll continue to build the <TourStop /> component.

Create the tour building block

The <TourStop /> is the main component of the product tour. It’s responsible for rendering the stop Popover next to the child element it gets in the props, according to the tour state.

Let’s start by creating a component that consumes the state of the store.

import React from "react";
import { useTourStore } from "./tourStore";

export function TourStop({ id, children }) {
  const { getCurrent, next, skip, prev, isLast, isFirst, isStepActive } =
    useTourStore();
  const { content } = getCurrent();
  return children;
}

We now want to show a popover next to the children.

The Popover component requires an HTML element to stick to. We will use a ref which we will add to the children props, and attach to the Popover.

It should now look something like this:

import React from "react";
import Popover from "@mui/material/Popover";
import { useTourStore } from "./tourStore";
import { cloneElement, useRef } from "react";

export function TourStop({ id, children }) {
  const ref = useRef();
  const { getCurrent, next, skip, prev, isLast, isFirst, isStepActive } =
    useTourStore();
  const { content } = getCurrent();

  return (
    <>
      {cloneElement(children, {
        ref,
      })}
      <Popover
        id={id}
        anchorEl={ref.current}>
        The step content
      </Popover>
    </>
  );
}

Now we want to show the actual stop popover, with “next”, “prev” and “skip” buttons, along with the content of the stop, and we only want to show the popover if the given stop is the active one! This is also the time to style a little our Popover so it will look the way we want.

<Popover
  id={id}
  open={isStepActive(id}
  anchorEl={ref.current}
  anchorOrigin={{
    vertical: "bottom",
    horizontal: "left",
  }}
>
  <div style={{ padding: 12 }}>
    <Typography component="div">{content}</Typography>
    <div style={{ display: "flex", justifyContent: "end" }}>
      <Button color="inherit" onClick={skip}>
        Skip
      </Button>
      {!isFirst() && (
        <Button color="secondary" onClick={prev}>
          Previous
        </Button>
      )}
      <Button color="primary" onClick={next}>
        {isLast() ? "Done" : "Next"}
      </Button>
    </div>
  </div>
</Popover>

We now have a working TourStop!

Adding Tour Stops to our App

Now, that we have all of our building blocks, it’s time to connect it all.

Let’s start with an element we want to attach to the first stop.

<IconButton
  size="large"
  edge="start"
  color="inherit"
  aria-label="menu"
  sx={{ mr: 2 }}
>
  <MenuIcon />
</IconButton>

We will wrap this element with a TourStop component, along with the id of the stop. This will tell the TourStop to appear if the current stop is the given id.

<TourStop id="side-menu">
  <IconButton
    size="large"
    edge="start"
    color="inherit"
    aria-label="menu"
    sx={{ mr: 2 }}
  >
    <MenuIcon />
  </IconButton>
</TourStop>

We can see that the stop is now appearing next to that element:

All we have left is to add the rest of the stops wherever we want!

The good thing about this explicit approach is that the code is very straightforward. When we look at the element’s code, we see it’s wrapped in a TourStore so we understand where the Popover is coming from. There’s no magic or voodoo (Like there was if there was something else listening to CSS Selectors to appear and injecting the Popovers to the DOM).

The downside is that the app components are “aware” of the Tour, so it’s not “clean” as it could be. And if there are multiple tours going on, it can get very messy.

Advanced topics

As you see, we created a very basic Product Tour, but it can be easily extended. Here are some common topics you may need to deal with:

Start the tour manually (don’t start by default)

This is done by a very small modification to our store. All we need to do is to change isShown to false, and to add a function show, that will reset the index to 0 and show the tour:

show: () => set({ isShown: true, index: 0 }),

Then we can consume the show function whenever we want to show the tour.

Force the user to use the guide

If we don’t want the user to interact with anything besides our tour, we can add a Backdrop element. Most component libraries have a “Backdrop” component, so all you need to do is wrap the Popover with this Backdrop component!

Remember the user’s position in the tour for the next app visit

Since we use Zustand, we get that ability in no time, by using the persist middleware.

This is helpful because if the user clicks “Skip”, “Done”, or leaves without completing the guide, we can continue just where we stopped the next time they visit.

import { create } from 'zustand'
import { persist } from 'zustand/middleware'

const useTourStore = create(
  persist(
    (set, get) => ({
      ...
    }),
    {
      name: 'tour-store'
    },
  ),
)

Pretty easy to modify things when you do it all yourself, isn’t it?

Summary

Today we learned how to build a Product Tour within minutes, while having maximum customization to use our components and styles.

I hope this is going to be useful for you and to save you time and money.

You can find the complete code here - https://github.com/omers4/react-product-tour-example

Bon Voyage!

Jan 2 2024
SoftwareArchitecture

A sweet (pun intended) article about what I learned from the book "Monolith to Microservices" regarding cohesion, coupling, and refactoring methods, demonstrated with real-life examples from a chocolate factory.

Prologue - How Did I Get the Idea for This Article?

In 2021, I read the book "Monolith to Microservices" by Sam Newman (2019). The book explains how to transition from monolithic systems to microservices architecture. I read it because, in my team back then, we worked on a huge monolith, and I had a task to write the first new microservice. I had only heard the buzzword "Microservice" before but never really understood its real significance until I read the book.

Since then, I've had the idea of passing on the insights I gained from reading this (245 pages long!) book, clearly and easily.

I wanted to allegorize this architectural problem with real-life examples. After all, software engineering is just like other engineering industries, except for the fact that the solutions we build are so easy to manufacture and deliver.

The problem was that the internet is full of examples of how Microservices architecture is just like a modern car factory and how high coupling is just like how a submarine is built, and well… As a stereotypical girl, this couldn’t be further from my world of associations!

So I realized it could be demonstrated with something much more appealing - a Chocolate Factory!, inspired by one of my favorite movies ever - Charlie and the Chocolate Factory (The 2005 version with Johnny Depp, obviously).

Willy Wonka and the Chocolate Monolith

When Willy Wonka just founded his Chocolate initiative, he did all the work himself. He bought the chocolate, made the candy bars, crafted the coffee toffee candies, wrapped them, and probably also took care of packing and selling.

It all worked for him, and the sales were nice, so the initiative turned into a little chocolate shop with a few employees. Not a long time passed before Willy Wonka's business became a successful company, so he decided to open his factory.

However, as time passed, some things started to squeak. Wonka noticed that the time to market is longer than expected, that there are bottlenecks in the process, and don’t get me started on those lazy Oompa-Loompas. In addition, it’s not as easy as it was to sandbox new candy technologies, for example, exchanging the cocoa beans manufacturer or composing a new gum that tastes like a whole meal (I’m still craving this one!).

So Wonka decides to make a change, break the Monolith, and transition his factory to a Microservices architecture.

Wisely, Wonka did not choose this architecture right from the beginning. When launching a new start-up, you often don’t know yet the domains and the different processes that will drive your business; therefore, it’s better to wait a bit until you’re done pivoting and the product structure is stabilized.

Let’s Move to Code

Assuming our factory is virtual, it probably looks something like this, with a few threads running independently.

class ChocolateStore {
	void RunStore() {
	  while(true) {
	    // Take care of orders
	      var order = pullOrder();
		  var chocolateBars = chocolateBarsStorage.Read(order.barsCount);
		  var toffees = toffeeStorage.Read(order.toffeeCount);
		  Ship(order.address, chocolateBars, toffees)
	  }
	}
}

class ProduceChocolateBars {
	void ProduceBars() {
	  while(true) {
	    var order = pullOrder();
		  var chocolate = chocolateStorage.Read(5);
		  var chocolateBars = new ChocolateBars(chocolate);
		  chocolateBarsStorage.Save(chocolateBars);
	  }
	}
}

We can identify a few problems here:

  • If we wanted to try a new cocoa beans provider or a roasting technique, this would be almost impossible, as every CandyProducer is reaching directly to the chocolate storage. Meaning, there’s a high coupling between the production and the storage.
  • If the chocolateBarsStorage has high traffic all of a sudden, it will slow down the entire order cycle. Meaning, it’s not scalable.

Planning the Factory Refactor

The first step after deciding to split a monolith is to decide on a metric, a number we will use to prove that the transition worked and was worth the effort.

In Wonka’s case, it can be the factory’s annual revenues, the amount of new candies invented per year, or even employee satisfaction grade. The important thing is to focus on the metric and the goal we would love to improve the most.

The second step is to start and untie the knots and understand the different domains of the business. Each such domain will move to be a new department in the factory (or a new microservice in our case).

Eventually, we want to hold a map of all the business domains and the relations between them. To achieve that, we can use something the book calls an “Event Storm”, meaning, listing all “events” we can think of that happen within our business.

For example:

  • An order is made from a store
  • A chocolate bar is finished wrapping
  • A truck is shipped to a store

After we have that list, we can start and draw our map. In our case, it should look roughly like this:

Each such circle is a different domain, that can potentially become a separate service.

However, just because there are different domains, doesn’t mean everything should be separated immediately—quite the contrary. We would like to gradually change the structure to examine the change and improve as we go, refactoring one piece at a time.

At each step, we will assess which part will give us the best ROI for transitioning it into a service. that would be a combination of 2 factors: A low amount of incoming edges, which reflects that no other part relies on them (like in the Inventing Room), and a high frequency of changes or issues, or high motivation for a logic replacement. For example, if we know that we want to change the way we take orders, the Order Management service can be a good fit.

Migration Methods

Now that we decided on the first service we are going to split, we need to decide on our strategy. There are a few refactoring methods that are useful in our case.

Strangler Fig

According to this method, we will write a new piece of code, and the old and new code will live side by side for a while, as the new code will start to handle more and more of the traffic as time passes, allowing us to gradually deploy our change.

How it’s done?

First of all, we will create a new service, in this case for our Order Management. We can even deploy the new service right away - we don’t need to implement anything yet because no one is aware of this service!

Now we can continue to identify the service’s interface, just returning 501 NOT IMPLEMENTED on each endpoint. In our service, we only have one endpoint in our interface: Place order.

As you notice, it’s all about short-lived branches in this method, which I like (will elaborate more in the end).

After we implement the logic of placing orders, we can start referring our customers to use the new ordering service instead of the old one. To utilize that we will use a Proxy, which upon an environment variable/feature flag will refer all calls to the old service, to the new one.

Here's an example of an Nginx proxy configuration we can use:

location /order-management/ {
    proxy_pass http://order-management/orders/;
}

Bonus fact: The name Strangler Fig comes from the Strangler Fig plant, which wraps around a tree, and strangles trees slowly until it kills their hosts 😲. Sounds like material for a horror movie.

Branch by abstraction

This method is adjusted to cases where the monolith is dependent on the service we are transitioning, which is the case we will face most of the time.

Let’s take for example the Chocolate creation service.

Currently, in our code, whenever some process needs chocolate, they just go ahead and take chocolate from the storage.

var chocolateBars = chocolateBarsStorage.Read(5);

This makes the task of building a “Chocolate creation” service seem almost impossible.

So we’ll start by identifying all of the consumers of chocolate, and replacing them with an abstraction.

By an abstraction I mean, an interface that will give us the same results, but the consumers won’t be aware of its inner implementation. For example:

interface IChocolateProvider {
   List<Chocolate> GetChocolate(int amount);
}

Now we can implement the abstraction. One implementation will act exactly like the code is acting now, and the other one will refer to our new logic, which is not implemented yet.

We will also use a factory method (oh boy, too many factories today) to return us the correct implementation.


class OldChocolateProvider: IChocolateProvider {
    List<Chocolate> GetChocolate(int amount) {
         return chocolateBarsStorage.Read(amount);
    }
}

class NewChocolateProvider: IChocolateProvider {
	List<Chocolate> GetChocolate(int amount) {
        throw new NotImplementedError();
    }
}

// Factory:
IChocolateProvider GetChocolateProvider() {
    return new OldChocolateProvider()
}

The third part will be to replace all usages of chocolate, to use the abstraction.

var chocolateProvider = GetChocolateProvider()
var chocolateBars = chocolateProvider.GetChocolate(5);

This is the hardest part of this refactoring method, and now that the code is well-refactored, we are free to start implementing our new service, keeping again on short-lived branches.

Only when we are done implementing our service, we can start referring calls to our new implementation, changing only one place - the GetChocolateProvider! We can make it rely on a feature flag, a configuration, etc.

And of course, don’t forget to remove the old implementation once you are done with the process, to keep your code nice and clean.

A word on short-lived branches

It’s tempting to develop a feature in a huge branch and merge it only when the feature is done. But it has multiple advantages for keeping your branches small, even at the price of having TODOs and TBDs in your codebase.

First, it’s helping with code reviews. It’s very intuitive that the smaller the change, the easier it is to review. Secondly, it reduces the chances of huge merge conflicts, in case you are working in an existing code base. Lastly, you get to receive early feedback, both from reviewers and from CI, tests, and automation, allowing you to improve along the way and get the best result eventually.

To summarize

I hope you liked this article and the unique perspective on the microservices transition!

I still haven’t decided if there will be a part B for this article that focuses on how to split Databases as part of the Microservices transition. If you feel like it can help you out - don’t hesitate to let me know

Dec 03 2023
Software

If you're launching a B2B/B2B2C startup, your Management Console is a crucial part. It's the user-friendly interface to monitor and manage resources and consolidate information. We're focusing on Web Management Console apps, the go-to for SaaS products.

With six years in B2B/B2B2C Web Management consoles, I've noticed common features across all platforms. I've been involved from mature stages, like Microsoft Cloud App Security (Adallom post-Microsoft acquisition), to building almost from scratch at Island.

(In the screenshot: Microsoft Cloud App Security. picture from Google)

These experiences give me a solid grasp of essential features for each development phase: the essentials, features for Enterprise-readiness to compete with industry leaders, and the less glamorous ones you'd ideally exit before you need to deal with (💰).

In this article, I've organized these insights into a pyramid. Not every company needs to reach the top; it depends on your product (a "feature" or a "platform"?), your goals (🦄?), and your clients (Small businesses vs. Corporations/Governments).

Let's get into it because, as you'll see, there's a lot to cover!

The Essentials

This lays the groundwork, shaping your product for businesses.

Account Management - Every business using your product needs to register and log in, whether through a user/password system or alternative methods like social login or OTP. You can either handle logins internally or utilize a service like Auth0.

Security (by design) - Maintaining robust security is critical, especially for B2B Management Consoles. Beyond fostering trust and ensuring continuous operation, you'll handle sensitive business information. If security isn't your expertise, consulting is wise for building features with security in mind. This involves validating and sanitizing all inputs and outputs, implementing CSP policies, and protecting against DDoS attacks.

Multi-tenancy - Ensuring each business ("tenant") accesses only their data is crucial. With multiple clients sharing computing resources, careful consideration is needed to maintain optimal separation.

Feature Flags - Implementing a feature flags system is vital for managing different product versions and release cycles. It enables testing features in production before full release. Numerous vendors offer feature flags mechanisms, making in-house implementation generally unnecessary.

Progressing Further

Visibility and governance become crucial as you move forward in the Management Console's development.

Queries Infrastructure - The ability to dynamically filter data via API from day one is essential. This prevents bottlenecks by avoiding the need for backend endpoints for every query. GraphQL is a recommended solution.

Visualizing Tools - Tables, grids, charts—most data needs representation in these forms. Choosing UI libraries that balance customization with features is vital. Don't overlook the importance of effective filters.

Analytics Infrastructure - Create insights and alerts from data, either periodically or in (near) real-time. Early implementation enhances the value customers derive from your product.

Winning Customer Appreciation

Make your product user-friendly and enjoyable for a positive customer relationship.

Excellent Navigation System - A user-friendly navigation system ensures users effortlessly find what they need. Features like in-site search, keyboard shortcuts, and "recently viewed" pages contribute to a seamless experience.

Customer Service - Prioritize customer service to address issues, collect feedback, and handle feature requests. While some services like Intercom offer a built-in chat feature, it's not mandatory.

Notifications - Communicate changes, new features, and updates to customers via email or within the console. This conveys continuous improvement.. If you’re in a rush, you can use a vendor like Beamer.

Documentation - While the product should be self-explanatory, user-facing documentation guides less techy users through basic flows. Links from each page to relevant documentation pages enhance user understanding.(Examples for vendors are Elevio, Readme which is more developer-oriented, and Document360)

Enterprise-Ready Features

Large corporations have their own established systems for various functions. Integration with these systems becomes crucial.

IDP SSO (Single Sign-On) - Integrate with the organization's existing user authorization systems, such as Azure AD, Okta, or Ping Identity, using protocols like SAML or OpenID. Have no idea what those are? good, because I have an article specifically about that!

Outbound Integrations - Allow users to view data in their systems through outbound integrations. This involves mechanisms like Webhooks and data export capabilities, like REST APIs. Services like Zapier and n8n simplify creating such integrations.

Inbound Integrations - Provide users with a convenient way to update product configurations without accessing the console directly. This includes user-facing REST APIs, terraform, and importing data using files. Another example is SCIM, a protocol for transferring identity information between parties.

Challenges for Giants

Reserved for handling complex tasks when dealing with big clients like governments and large corporations.

RBAC (Role-Based Access Control) - Implement a mechanism that restricts access based on a person's role within the organization. This can be tedious, as it's an ongoing process with new endpoints and UI elements continually added.

Multi-Region and Multi-Cloud - Accommodate requests to store data in specific regions or on preferred cloud providers as your startup expands.

Compliance - Address compliance requirements early on, covering aspects like GDPR, advanced accessibility features, avoiding logging PII, and undergoing regular security reviews.

In conclusion

While the list may seem extensive, it provides a comprehensive view. Analyze your product, user persona, and target businesses to select features relevant to your Management Console. The key is Balancing third-party solutions for efficiency and in-house implementation for customization. Feel free to reach out for advice tailored to your specific situation.

Nov 11 2023
Management

OMG, I’m a manager. Now what?

Starting a new engineering management position for the first time can be overwhelming - I would know! Suddenly, you have many things to consider, such as keeping employees happy and satisfied, reaching business goals, and completing your own development tasks.

It's important to adapt quickly to your new role, but don't get too stressed. Fortunately, the field of business management has been extensively researched, and there are many experienced individuals and books that offer valuable insights.

In this article, I recommend three books that I read during my first management position in the industry. These books cover various aspects of management, from the significance of empathy and personal growth and the importance of teamwork to "cold" topics like delivery, goals, roadmaps.

What's interesting about these books is that none of them directly discuss engineering or software management. However, that's the beauty of it. Unlike learning a specific tech domain, which may become irrelevant in a few years, management is all about people. People are the same all over the world, making management a universal skill.

So let's learn from the best in the field from all around the world 🙂

Mindset - Carol S. Dweck, 2006

The first book on this list is not one you would typically find in the "Management" section of a bookstore, but rather in the "Self-help" or "Psychology" categories. However, its inclusion here is intentional.

"Mindset" presents a simple yet powerful idea. Our mindset, or how we think about our abilities and skills, greatly influences us. People with a "Fixed mindset" believe that their skills are fixed and that they are inherently good at some things and bad at others. On the other hand, people with a "Growth mindset" believe that they can continuously evolve and improve in all areas.

While this may sound "New Age" or naive, it is worth noting that the author, Carol S. Dweck, holds a Ph.D. in Psychology and has conducted extensive research on this subject for decades. Her research shows that individuals with a Growth mindset tend to achieve greater success, regardless of their starting point. As the book states, "It's not always the people who start out the smartest who end up the smartest."

I believe this book is essential for you managers, because in order to foster personal growth and help individuals reach their full potential, managers must first recognize that everyone has potential and can learn and grow in various ways, including yourselves. Along the way, this mindset shift can also have a positive impact on your own lives, as they discover new possibilities for personal improvement.

Imagine how your team might change if you assign a backend task to a developer who claims to be only skilled in frontend. Consider how you could improve by placing yourself in challenging situations, such as speaking in front of an audience. The possibilities are limitless.

By the way, all the books I have read are in Hebrew, my mother tongue. One thing to note about translated Hebrew book titles is that they are often loosely related to the original name and can sometimes be ridiculous. For example, the Hebrew title for "Mindset" is "The Power of Determination" (🤦🏻‍♀️).

The First 90 Days - Michael Watkins, 2003

Hebrew name: “90 days of grace” 🤦🏻‍♀️🤦🏻‍♀️

This book takes a different approach compared to the previous one. Instead of focusing on effort and improvement, it emphasizes the importance of achieving business results. It highlights the significance of the first 90 days in a new position, as they can determine the success or failure of the entire role. The book provides insights on various factors that require close attention during this critical period, along with multiple examples from individuals who have started new management positions.

The book addresses cleverly common challenges that arise when starting a new role, such as the temptation to make immediate changes, lack of familiarity with the company's history, and neglecting relationships with colleagues.

At times, the book can be intimidating as it urges readers to start thinking about early quick wins from day one. These wins will grow over time and establish one's reputation as a successful manager. However, the reality can be daunting. In a new job, you are responsible for making it work without much guidance or support, so it’s good that the book doesn’t sugar-coat it.

One drawback of the book is that most of the examples focus on individuals hired from outside the company. It lacks guidance on how to navigate a promotion from within, where there is already a history and established relationships.

Personally, I find it beneficial to revisit this book once a year. It offers valuable self-assessment exercises that help identify strengths, weaknesses, and guide future development.

The Five Dysfunctions of a Team - Patrick Lencioni, 2002

Hebrew name: "Light up the fire" 🤦🏻‍♀️🤦🏻‍♀️🤦🏻‍♀️

"Not finance. Not strategy. Not technology. It is teamwork that remains the ultimate competitive advantage, both because it is so powerful and so rare."

While the previous book focuses on the manager, the individual, "Five dysfunctions of a team" focuses on the team as the main entity that should improve as one, as the main way to achieve business goals.

It is also the only one in the list that is somehow related to the software industry, as it tells a story about a start-up company in Silicon Valley. It's a story that many of us can relate to and smile about when one of the characters is too similar to someone we know from work.

In the book, Lencioni describes 5 common pitfalls that teams experience, with one leading to another. He claims that even if the individuals in the team are highly skilled, the team will not be able to function in the long term if there is no trust between teammates, if teammates prioritize their own personal goals over the team's goals, and more.

This is a very interesting and refreshing approach, especially for engineering teams, where each individual often works on a different feature (in many companies there is even a degree of “Individual Contributor”!). It's easy to forget that it's a team effort and not just a collection of individuals. I believe we have all been in situations where someone updates us on what they're doing in the daily or planning, but no one else listens because it doesn't seem relevant to them.

As it is the most recent book I've read out of the three, I'm curious to see how it will influence my leadership style. It definitely sheds light on areas that go against my nature, such as embracing conflicts and arguments as main tools of discussion (I dislike arguing!), the importance of being vulnerable (I tend to keep a distance from those I manage), and the ability to hold colleagues accountable if they have made mistakes.

Bonus: Atomic habits - James Clear, 2018

Hebrew name: "Atomic Habits" (Thank you!!!)

Although not directly a management book, I highly recommend reading "Atomic Habits." When you become a manager, your life suddenly becomes much busier, and you need to equip yourself with methods and practices because what has worked for you in the past may not continue to do so.

For instance, if you used to rely on your memory to remember things, now you have six times the amount of information, tasks, birthday dates, 1:1 summaries, and personal information of your employees to remember. Can you still rely solely on your memory?

Another example is your free time. Your day used to be dedicated to coding, but now it is filled with meetings. How will you make progress on your coding tasks with the limited 30 minutes of free time you have each day? And how will you find time for the things you love in order to, well, not lose it?

"Atomic Habits" proposes a simple idea: our good habits tend to be the ones that give us immediate positive feedback (like smiling at each other), while the bad ones are comfortable in the moment but have negative consequences that we often don't realize until it's too late (like eating a pizza every night). Take a moment to think about your habits and see if this rings true.

When it comes to habits, our bodies are programmed in a specific way (based on the release of dopamine hormone), but we can still have a significant impact on our habits by understanding what motivates us. And that is precisely what this book offers. It provides simple tricks and methods to help us create new, positive habits that lead to happier, healthier, and more meaningful lives.

The book emphasizes taking small steps and making slow progress until the habit becomes ingrained. As the author says, "Every action you take is a vote for the person you wish to become."

Conclusion

In conclusion, continuous learning is crucial in everything we do, especially when starting a new role. As new managers, there is a lot for us to learn, so it's best to start early.

Books are not the only source of learning, of course. It is also beneficial to find a mentor or someone to tutor you, either from within or outside the company.

I hope you enjoy reading these books and that they have the same transformative impact on your perspective as they did on mine.

Oh, and if you come across any other good books, please let me know!

Sep 30 2023
SoftwareBackendSecurity
SSO (Single Sign-on) is not rocket science, but learning it can be a bit overwhelming.
It involves various terms such as Idp, SP, RP, and authentication methods like OIDC and SAML, along with somewhat complex flows (hi, OAuth!).
The available information on the internet regarding this topic is often wordy and verbose.
I realized that it could be explained in a simpler and clearer way.
So I created a nice infographic, which also allows me to practice my Figma skills, win-win :)
2 main methods for implementing SSO-These are examples for SP-initiated SSO, There’s also Idp-initiated SSO, where the first request is made directly to the Idp
Jul 21 2023
Software

Seriously? Another Clean Code article?

Well, yes and no.

There are some Clean Code principles that we all know by heart. In every code review, we take pride in comments like "reuse that code", "give a better name to that variable", and "split this function into two".

However, knowing these principles doesn't guarantee we write good code.

Often, especially in junior developers' code (but not exclusively), some Clean Code principles are overlooked and forgotten. These are the principles that are more subtle and may be harder to spot in a code review or in your own code, but they are the ones that can make your code truly good.

While today's development tools, such as linters and Resharper, make it easier to detect "code smells" (although these are still important to understand), most Clean Code articles tend to focus on these principles.

Therefore, this article focuses on the Clean Code principles that cannot be automatically detected by a linter or Resharper. These principles are crucial for writing truly good code.

First things first, what is clean code and why do I need it?

Clean code is a philosophy that focuses on writing code that is easy to read, understand, and maintain.

It is a set of coding style guidelines that are widely popular within the development community. The philosophy is based on the idea that not only the computer should be able to understand your code, but also those who will need to read and modify it in the future. Additionally, the goal is to create more robust code that functions more effectively.

The principles

Always try to explain yourself in code

I'm not sure why, but when I studied software development, it was somewhat trendy to document everything and was considered a good practice. Every method, every variable had to be documented.

// This function makes the sound of a woof
function Bark(Dog dog) {
    // dog is a Dog object
    print("woof")
}

It took me years to understand that good code explains itself with clear variable and function names, making comments unnecessary.

Why is that?

First, comments make it easy to write unclear code. You rely on your English skills to emphasize what you meant to code, so whoever reads your code has to read the comments first, making every interaction with the code time-consuming.

In addition, comments tend to become stale as the code changes over time, but we don't always remember to update the documentation.

If that's not enough, comments also take up a lot of vertical space in our code, which is already limited.

Of course, there are some exceptions. If there's a very unique functionality, an unusual solution to a problem, or an unexpected result, it's better to explain it than to leave your reader wondering. In general, you could say documents are good to explain “why”s, not “what”s. e.g, why we chose a certain data structure and not what the function does.

// this is calculating the space between the container to the end of the screen

// This is a temporal solution until gets fixed next sprint - ticket OS-12345

// If you remove the next line, the world will collapse, because x, y, z

Additionally, if we are discussing generated documentation, for example for public API, comments are necessary.

Prefer polymorphism over conditionals: if, else, switch

If you encounter a situation where you need to act differently multiple times based on the value of a variable, such as in a Zoo app where you need to treat each animal differently, you may be tempted to handle it quickly and easily-

getAnimalFood(string animal) {
   animal == 'dog' ? 'dogfood' : 'fish'
}

getAnimalSound(string animal) {
   animal == 'dog' ? 'ruf ruf' : 'meee'
}

One issue with this approach is that every time we add a new animal to the system, we will need to change all of these functions. This means we are not reusing the "animal == 'dog'" condition, which makes it difficult to maintain.

To solve this problem, we can apply the "Tell, don't ask" rule. Instead of asking an object about its state (e.g. "are you a dog?"), we tell it what to do (e.g. "give me your food").

To implement this, we can use a factory and interfaces.

interface IAnimal {
   string getAnimalFood()
   string getAnimalSound()
}

class Dog : IAnimal {...}
class Cat : IAnimal {...}

getAnimalFood(IAnimal animal) {
   animal.getAnimalFood()
}

getAnimalSound(string animal) {
   animal.getAnimalSound()
}

class AnimalFactory() {
  getAnimalByName(string name) {
	    name == 'dog' ? new Dog() : new Cat()
   }   
}

Notice that even if we still have one condition in the factory, we will only need to change it once for every new animal!

Base class should know nothing about their derivatives, A.K.A Principle of Least Knowledge

This is one of the most common mistakes made by junior coders.

Consider a variation of the previous code. We now have a React component that represents an animal. Although it's not discussing inheritance, the concept is similar.

function Animal({ name }) {
	const sound = name === 'dog' ? 'Woof': 'Unknown'

  return <button>Click me to hear {sound}!</button>
}

...
<Animal name='dog' />

This pattern is one of many that creates coupling between modules. In this case, the implementation of the Animal component is coupled to the implementation of Dog. That means if we change Dog's implementation or add a new animal, we will always have to keep Animal's implementation in mind.

With just two modules, it's not that bad, but as the number of modules increases, it becomes difficult to remember what is coupled with what, making it error-prone.

Moreover, it's harder to test the code like this because it doesn't have a single responsibility. If we want to test the Animal component, we don't want to guess what name to give it in order to render the correct sound. We don't want to test its ability to determine the sound from an animal; we just want to ensure it's rendered correctly based on the props it's given.

Therefore, it's better to tell the object what to do instead of asking about its state.

function Animal({ name, sound = 'unknown' }) {
  return <button>Click me to hear {sound}!</button>
}

...
<Animal name='dog' sound='woof' />

So much cleaner, right?

And whenever a new animal is added, we won’t need to add code in the Animal component itself.

Bonus: Always look for the root cause of a problem

This is more of an abstract concept and doesn’t really provide code guidelines, so there are no code examples here.

When investigating a bug, don't just put a Band-Aid on it without really understanding what the root issue is. This applies both during the investigation process and when writing the solution.

Red flags that indicate you're not looking for the root cause include leaving comments like "I don't know why, but this solves the bug”, “Removing this line has unexpected results" or adding a too-wide try-catch to solve a problem.

If you don't delve into the details of an issue, it will come back to haunt you like a boomerang when the issue inevitably arises again. So it's better to put more effort into understanding the root cause from the beginning and getting things right.

Conclusion

I hope that you have learned some new clean code principles, or have been reminded of them. I believe that applying these principles can significantly improve your coding and reviewing skills.

There are many other principles that I like, such as "else-less code" and the importance of dependency injection. I encourage you to look for more as the internet is full of information about this topic. I specifically recommend https://refactoring.guru/, which provides simple explanations and examples for every "code smell."

The next time you review code, don't settle for a simple "split this function" comment. Take the time to clean and tidy up the code. I'm sure it will pay off in the future, for both you and your team.

Dec 10 2022
SoftwareFrontendBackend

We, software engineers, are lazy, and thank god for that because it sometimes leads to some great solutions. It’s a special kind of laziness because we are willing to work hard on something to ensure that in the future we won’t do any job that is repetitive, robotic, or boring. In other words, we automate everything.

So why not start with our development process?

In this article I’ll show you a pattern, a method I used several times at work and it accelerated my features dev velocity by creating a “UI factory”, a machine that produces new UI components in no time. I call it “Externally-configured UI”, or in short, ECUI.

*The examples will be demonstrated using ReactJS, but the method itself is infrastructure agnostic.

What is Externally-configured UI in comparison to declarative and imperative UI

There are 2 paradigms in software development in general and in UI specifically: Imperative programming and declarative programming.

In UI frameworks that are imperative, you change the state of the view by modifying an object representing it. This approach is common in more traditional frameworks like WinForms (what nostalgia!) and in vanilla JS. For example-

MessageBox box = new Box();
box.title = "alert";
box.Show();

In new modern UI infrastructures (ReactJS for example) the UI is written declaratively, meaning you declare the view structure, and its state is changing based on the received data. For example-

return <MessageBox open={isOpen} titleaa={title} />

Externally-configured UI is using declarative UI where the declarations are not part of the UI codebase and are agnostic to the frontend infrastructure. In other words, they are externally configured.

The UI itself is only responsible for rendering the configured declarations, so new additions will be added with no code.

An example

Imagine you are required to develop a feedback form for your web application, in order to receive feedback from your customers.

You get an exact specification from your product manager: the form consists of 2 questions:

  1. How well is our service? (rank from 1 to 5)
  1. Please describe (free text)

How would you reach that kind of problem?

Well, you would probably first develop the building blocks, which are ranking input and free text input. Then you will build the form itself, and add the building blocks inside (Yay composition!).

At this point, your form probably looks something like that-

const FeedbackForm() {
	return (<form>
		<FormField title="How would you rank our service?">
			<StarsRanking max={10} />
		</FormField>
		<FormField title="More comments?">
			<TextArea max={8} />
		</FormField>
		<input type="submit">
			Submit
		</input>
	</form>)
}

It may seem like a non-issue, but you’re not done yet!

Because for every request to add an input, or change the order of inputs, you or one of your team need to do a manual change in the codebase. to open a pull request. to wait for CI. It’s becoming time-consuming, and most importantly, boring. we don’t like boring.

So what do we do?

We basically make our form render the inputs based on an outside resource, describing the metadata of each input that should be rendered in the form.

// declarations.json - this is what shapes the form
[{
	"type": "stars",
	"title": "Please rank your experience from 1 to 5",
	"name": "experience",
	"max": 10
}, {
	"type": "freeText"
	"title": "Any other feedback?"
	"name": "feedback"
}]

We also provide our product managers with the ability to change that resource. In other words, we externalize the configuration of the UI. Now we are free to go back to doing the real challenging work!

In conclusion, Externally-configured UI is good for-
  • Speed: It helps you run fast, and contributes to the business needs because additions and modifications are done without the need to write code.
  • Removes bottlenecks: Changing configurations requires no special expertise in any technology, which allows developers from other teams to practically change the UI even if they are backend oriented, for example. Moreover, non-technical people can change the configuration too if we’ll make it friendly enough. This helps to reduce bottlenecks between teams.
  • Happy engineers: Since different personas can also handle the configuration, even the non-technical ones, that means that the R&D can focus on the more complicated tasks they love so much.

So how does it work?

Let’s try to build our user feedback form by familiarizing ourselves with the 3 parts of ECUI: Declarations, Views, and a factory.

The Declarations

The declarations contain your UI declarations. They should contain the minimal amount of data possible to render the UI correctly.

  • The schema of the declarations should be very simple, as it needs to be modified sometimes by less technical workers. Ask yourself who will edit the declarations and choose the format accordingly (I love JSON because it’s convenient to work with in JS & TS but also easy to understand).
  • The declarations can sit anywhere you want: I recommend a file in VCS in order to keep version history and to allow a CI/CD process, but you can also store it in any remote storage that provides editing, such as a database, S3 bucket, etc.
  • Make sure to include some basic tests and schema validation on the declarations file, to avoid misconfigurations that will make the UI break. (for example, so you won’t use an input type the UI doesn’t know how to render yet, that you didn’t put the same input twice, etc.)

The views

The components composing the UI, the parts you used to constantly copy-paste.

The views/components may share common properties, like name and title, and some of them have unique properties. Design the components in such a way, that if a common property is added, it will be added in all of the components at once.

<Textarea name='' title='' />
<StarsRanking name='' title='' max=10 />

The Factory

The factory is in charge of rendering the UI according to the received declarations.

It consists of a mapper and a view, that is using the mapper, to render the declarations.

const mapper = {
	stars: StarsRanking,
	textarea: Textarea
}

const FeedbackForm() {
	const declarations = useDeclarations() // Using an ajax request
	const fields = declarations.map(declaration => {
		const Component = mapper[declaration.type]
		return 
			(<FormField>
				<Component {...declaration} />
			</FormField>)
	})
	return <form>
		{fields}
		<input type="submit">
			Submit
		</input>
	</form>
}

And Voila! Not bad for a sloth.

Which features are suitable for Externally-configured UI?

If you think about using ECUI for a feature, Ask yourself 2 questions:

  • Do I repeatedly need to modify a certain feature?
  • Are the modification actions somewhat robotic and easy to do? meaning, I don’t need to develop new views and components for them, but rather copy-paste, and change data/icons/texts? Clue: if your brain wheels don’t move even a bit, this might be the case.

If you answered “Yes” to both of these questions, ECUI might be your best friend!

Cons of Externally-configured UI

If I already convinced you that ECUI is the best, just keep in mind that if you don’t do it carefully, it can become a huge mess.

In order to do it carefully, you need to define precisely who are the editors of the declarations, what qualifies them to be editors, what protects the declarations from breaking the UI, and what is the process of such modification (declarations review? CI? CD? etc.) That’s a lot of questions!

In addition, there is an initial cost for developing the UI factory itself, so you need to think well if it’s a problem that bothers you enough to automate it with configuration.

However, if the problem is bothering you and taking lots of your time for you and your team, I assure you it will pay off shortly.

Summary

Hope you enjoyed the article and that in the future it saves some precious time for you, your team, and your company.

Feel free to reach out with any questions or thoughts.

So… let’s get back to working hard, so we can go back to being lazy again!

Oct 28 2022
ManagementSoftware

When I was a developer, I remember watching my team lead running a planning or a retro meeting and thinking “that actually doesn’t seem this difficult - we just move Jira tickets from one place to another”. But when you sit in their chair, you understand that managing these meetings is a whole new world.

Suddenly, there are so many aspects to consider and you’re constantly trying to balance between business, people, and infrastructure: you juggle between contributing to the business needs, fulfilling everyone’s ambitions and abilities, and building a good product that is robust and easy to extend.

I collected some of the lessons I learned along the way, and hope it will help you too:

  1. XL features are the devil
  1. There’s never a good time for tech debt in the sprint, nor is there for bug fixes
  1. Distributing feature expertise is the best thing you can do, but also one of the hardest things to achieve
  1. Retrospectives are useless unless you keep getting back to them
  1. Ceremonies are the anchor of each developer’s day - keep them fun, short, and stable

1. XL features are the devil

XL features (features that take more than 1 sprint to complete) can look very attractive at a first glance: they can give a lot of business value at once, and are most likely to be challenging and interesting for the developers.

But, as the title may imply, they are the devil. And why is that?

First of all, it is hard to estimate them, which makes them quickly get out of control, and generally hard to manage. Such features usually consist of multiple technical tasks, each one having its own estimation. While it’s very natural to under-estimate the development of one task, when it comes to big features, the total estimation errors can sum up so the feature will take much more than we thought (XL → XXL!)

It is also not very agile to develop a whole feature without the crucial part of the customer feedback loop.

In addition, the developer actually feels frustrated that they don’t get their feature released, and they don’t see the end of it coming.

As long as a developer is assigned to an XL task, you don’t want to interrupt them with small tasks that will cause them to context-switch. However, you have to have the ability to do so, in order to fix urgent bugs, for quick wins, etc.

So what can we do? Insist to divide the XL features into distinct phases of 1-2 sprints at most, and release the feature at the end of each phase. This way, the feature will be easy to digest, even if this division is only psychological.

2. There’s never a good time for tech debt in the sprint, nor is there for bug fixes

I’ve noticed that unless you decide that x% of the sprint goes to tech debt issues and y% goes to bug fixes, they just don’t make it into the sprint, making them pile up for weeks in the backlog, and we don’t want that. Especially in start-ups, there are always urgent tasks that can help with the actual business, and impact clients (and that’s a great thing!).

Therefore, you have to insist to have a prefixed amount for these so important tasks in the sprint, even if a small amount. (for example, 25% a sprint for bug fixes, 10% for infrastructure and tech debts)

3. Distributing feature expertise is the best thing you can do, but also one of the hardest things to achieve

I noticed that the natural state that agile teams end up being in is that each developer is the complete authority for some features. It means that for every question one has, one will ask that developer. Each bug or feature request will automatically be redirected to that person etc.

While it sometimes makes it easier to manage the tasks this way, and it helps to create motivation and ownership feeling within the developers, we must have at least 2-3 additional experts in each feature, unless you are also putting impossible pressure on the developer.

It especially tends to happen if you have the “Feature owner” role in your team and increase in a full-stack team, where every developer comes with a little different background and professional experience (One is a backend only, the other never run SQL in their lives, and the other is 50-50).

So what I suggest to you is (I do the same) to make a table of all the big features in your team. Near each feature, write in one column the name of the owner, and in the other column, who else knows this feature? Your goal is to fill that table. Next time tasks arrive, think twice before you auto-forward it to the dev who knows it the most.

For example-

FeatureOwnerWho else knows?
Alerts componentDwightJim, Andy
Login serviceJim-
Web scraperPhillisBob

It’s hard, but I believe it creates healthier teams.

4. Retrospectives are useless unless you keep getting back to them

Next time you do a retrospective, just before you start taking notes, go over the previous retro notes with the team. What things have you already improved from this time? What things still need some more attention this week?

While taking notes of this sprint, always remember the past, and reflect that to the team: “It’s good that this sprint we did X well, because we brought up this subject in the retros a few times before”. This helps to motivate the team as they see the progress they make every time. Don’t rely on your memory, use written notes! (This is true for everything in life, regardless).

5. Ceremonies are the anchor of each developer’s day - keep them fun, short, and stable

In the unstable and fast environment of Agile, it’s good to have constant events in our calendar. they give us security and are the anchors of our day.

Remember to keep it light and fun (as much as possible), and most importantly - make them short (especially the dailies).

And a bonus one: make sure everyone understands the purpose of the Scrum Ceremonies.

It’s easy to forget why it is so crucial to have a daily and what it’s purpose. Have a session with the team explaining the target of each occurrence, what you expect from it and how is it helpful. Who knows, maybe you are growing the next scrum master right beneath your eyes.

Mar 12 2022
SoftwareFrontend

Intro

It’s 2022, web frameworks have developed so much, and still, whenever we need to upload files from our UI to a server, we panic a little bit.

This tutorial will learn how to upload files from ReactJS in no time to AWS S3, using a library called react-aws-s3.

Disclaimer: I recommend using this tutorial only when you are developing an internal tool, or for yourself. For “Real-world” systems, we usually want to monitor the uploaded files, block unauthorized users, etc. For that cause, we would use AWS API Gateway to get a signed URL. Read more about it here.

Requirements

For this tutorial, you will need -

  • An AWS account
  • Familiarity with ReactJS and a ReactJS app

Before we start

First, let’s answer the questions that keep our minds busy.

When should I upload the file? immediately after selection, or when submitting the form? In my opinion, when selecting the file in the files browser and clicking “open”. This allows us to display validation errors before the form is sent, therefore giving a better user experience, compared to the other option where the user gets validation errors when they are sure they finished filling the form.

Where do I store the file? The natural thought is “in the DB, with my other stored stuff”. The rule of thumb says that filed under 256K can be stored in DB, whether as a b64 encoded string, or using blob options of the DB. However, it is considered a bad practice for heavier files because of performance issues (slower queries and more RAM consumed by the DB) and cost issues. In addition, most DBs limit the size of each record, therefore limiting file size. for example, MongoDB limits document size to 16MB, leaving us no choice but to store the file elsewhere, if they’re bigger than that.

How do I send an HTTP request with a file, for god’s sake?!? Looks like it even needs a special content type and stuff! By uploading the file to a cloud storage service. We all know some storage services (Google Drive, Dropbox, Box) but I chose to use S3 of AWS thanks to the many SDKs it has for every platform ever (Hi Boto3!), plus it is considered relatively cheap (check out their free tier plan).

Naturally, there is already a library in ReactJS for interacting with S3, called https://github.com/Developer-Amit/react-aws-s3, so we don’t have to be aware of sending the request itself.

1. Create an AWS S3 bucket

In S3, files are called objects and are divided into buckets, each bucket has its own URL.

In the AWS management console, go to S3

Click on “Create bucket”, and fill in the details of your bucket:

In Object Ownership, select “ACLs enabled”.

Check or uncheck the “Block all public access” according to whether your files can be public or not (in my example they can be public)

Leave other settings as they are, and click “Create bucket”.

Now, navigate to your new bucket, go to “Permissions”, and edit “Cross-origin resource sharing (CORS)”. This is important in order for the ReactJS app to access the bucket.

Paste these settings, where AllowedOrigins should contain only your actual domain, we will keep “*” just while developing.

[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "GET",
            "POST",
            "PUT",
            "HEAD"
        ],
        "AllowedOrigins": [
            "*"
        ],
        "ExposeHeaders": [
            "ETag",
            "Accept-Ranges",
            "Content-Encoding",
            "Content-Length ",
            "Content-Range"
        ],
        "MaxAgeSeconds": 3000
    }
]

click Save.

Our bucket is now ready to use.

2. Create an AWS API key

In the top-right corner, click on your name, and then on “Security Credentials”. Then choose “Create New Access Key”.

Make sure to save the Access Key ID and the Secret Access Key in a safe place. Preferably, in a place where your app can use them, for example Kubernetes Secret, Github Secret, Heroku Config - depends on your CD environment.

Then in my app, I will store their values in Heroku. I will use the Access Key ID and the Secret Access Key as process.env.S3_KEY and process.env.S3_SECRET .

3. Upload a file from ReactJS

The moment we’ve all been waiting for!

Let’s install react-aws-s3, by running

npm install --save react-aws-s3
npm install --save buffer

Let’s add a component called FileUploader. This component contains a file input and a state for holding the file path and file name.

function FileUploader() {
		const [picName, setPicName] = useState(null);
    const [picPath, setPicPath] = useState(null);
    const [errors, setErrors] = useState(null);

    const upload = (file) => {
        // I do nothing!
    }

    return (
        <div>
            <input type="file" onChange={(e) => upload(e.target.files[0])}></input>
            <div>File name: {picName}</div>
            <div>File path: {picPath}</div>
            <div>Errors: {errors}</div>
        </div>
    );
}

export default FileUploader;

Currently, nothing happens when we select a file:

The next step is to add the S3 configuration and initialize the S3 client:

import S3 from 'react-aws-s3';
import { Buffer } from 'buffer'; // This is due to a bug in the library itself
window.Buffer = Buffer

const config = {
    bucketName: 'good-forest-static',
    dirName: 'imgs',
    region: 'us-east-2',
    accessKeyId: process.env.S3_KEY || '',
    secretAccessKey: process.env.S3_SECRET || ''
}

const ReactS3Client = new S3(config);

Add a callback for file uploading - notice you can use the result file after it is already stored and have an absolute path.

const upload = async (file) => {
    // here you can get file.name and file.size to add some validations
    setPicName(file.name)

    try {
        var result = await ReactS3Client.uploadFile(file);
        setPicPath(result.location)
    } catch (error) {
        setErrors(error.message)
    }
}

The next step is... oh wait, there is no next step. That simple.

Now, you can store in DB only the path to the file instead of the file itself.

Summary

We learned how to easily upload files from ReactJS to our cloud storage.

Happy uploading!

The full example can be viewed here:

https://github.com/omers4/react-aws-s3-example

Feb 25 2022
SoftwareBackend

In the previous chapter we learned what the term API really means, and identified the differences between internal API and customers API. Today we will learn about practical methods and approaches for implementation.

Types of HTTP APIs methodologies

Luckily, some good people already have thought of this question for us and have come with some very good solutions. Let’s go over some of them.

RESTful API

REST has become such a popular methodology, that it is often used as a synonym for HTTP API. You can hear a lot of devs and managers say “yeah, let’s write REST API” when they really just mean an HTTP API.

REST (Representational state transfer🤢) is a set of rules and constraints for designing an API. It’s not a protocol per se, but more of rule of thumb.

The idea behind REST is to manage resources. A resource is everything the user is handling (creating, watching, editing, removing). For example in our shows API, a TV show is an asset, but also an actor can be an asset, a producer, a movie, etc.

There are a lot of other principles on how to manage the API server (caching, layering and more), but since we are focusing on the interface, let’s examine that.

In REST, we always stick to the conventional http methods and http response codes. Like-

GET /show
200 {shows: ["gilmore-girls"]}

GET /show/gilmore-girls
200 { name: "Gilmore Girls", Actors: ["alexis-bledel"] }

And from here I can go on to get information about the actors of the show:

GET /show/gilmore-girls/actor/alexix-bledel
200 { name: "Alexix Bledel", Shows: ["A handmaid's tale", ...] }

Why is it good:

  • Our customers already saw this pattern thousands of times before
  • Supports classic CRUD operations on each asset (Create, remove, update, delete)
  • Fits perfectly if the user needs to traverse the entities. we can change the API and it doesn’t matter them

GraphQL

GraphQL is a very powerful tool for building APIs, with tons of buzz around it.

GraphQL (GQL) is a query language for your API. Today, each programming language has a GraphQL SDK that allows you to define a schema for your data in the database. It automatically translates GQL queries to the language the db understands (SQL, mostly). Pretty cool huh?

For example, if we want to get the names from Bojack Horseman TV shows, and the names of their characters, we will run something like

POST /graphql 

query TvInfo($show: Show) {
  show(show: $show) {
    name
    charachters {
      namec
    }
  }
}

{
  "show": "Bojack Horseman"
}

That’s right, the query is in the body of a POST request. Seems odd, but hey, so does HTML inside JS, and see where ReactJS is today!

I personally prefer not to use GraphQL for our cause specifically, because the learning curve is steep and it’s quite not intuitive, So I think it’s worth using only if your customers are heavy tech users, like developers.

Why is it good:

  • If you need to provide complex queries and possibilities over your data, you get that “for free”
  • If you don’t want to struggle with building the API and you just want to get it over with and go to the beach 😎 (not judging)
  • It has multiple SDKs for almost all databases and programming languages there are

Still not what you’re looking for? “REST-ish”, or Plain HTTP

In cases you want a simple API for your customers, but you don’t really need to perform CRUD operations on resources, you can compose yourself the HTTP endpoints and responses.

In this case, you have to be careful about the conventions you will be using. You will need to decide whether POST or GET, for example, for fetching data. On the one hand, GET is easier to send, because all the data is in one line. However it’s hard to pass complex parameters, and has a size limitation of 2,048 characters(!).

I would suggest you in this case to still be consistent with HTTP methods and error codes, to make it easier on you readers. that’s why I call it REST-ish.

In addition, it’s important in this case to make sure you fully understand the needs of the customers and how they will be using the API. If it’s already tailor made, it better be good, right?

Examples for such APIs-

  • Elastic search (TBD). Elastic search is an open source search solution that exposes some REST apis as well as APIs for the search, that like graphql sends the query inside the body.

Why is it good:

  • You can have special query methods that don’t fall in the category of any of the others, like searching in different databases and resources.
  • It fits for all customers, both technical and semi-technical
  • You control every implementation detail of it.

A case study: API for TV shows

Let’s say we work for some famous streaming service, and we have a job to export our data to analysts in another company that run some ML models on the data to produce insights.

The analysts in the company are somewhat technological, they can write basic scripts for example and to send CURL requests. (It doesn’t mean all analysts aren’t techies! chill)

The basic requirements from them are:

  • Be able to search TV shows by dates they came out, to filter by name and to sort by dates.
  • Receive the name and the rate of each TV show.

What method will we be using?

On the one hand, we have a DB resource, that is the TV show. so maybe we should use REST API.

On the other hand, the analysts need to filter data, that the proper model of REST don’t support. also they are not very technical so making them learn GraphQL won’t make my API very popular. REST-ish seems like the correct choice here.

Now that we decided to use REST-ish, we have 2 options:

The one is to have a GET method with start, end and query as query parameters

GET /search?start=12-10-1994&end=12-10-1994&query=gambit&sort=asc

The other is to have a POST method and to send the query parameters in the POST.


POST /search
{
  query: "gambit",
	start: "12-10-1994",
	end: "12-10-1994",
  sort: "asc"
}

Both of them are fine, the POST one is slightly more comfortable to extend than the GET, and the GET is more comfortable to send and to play with, For our goal I would say we should go with the GET here.

Summary

We have learned how to design an API interface that is facing external customers, to create a comfortable, intuitive, beloved API.

The principles we learned are especially important with clients but are effective with any other human reader, whether it’s a co-worker like a frontend developer that uses your calls or even yourself, and you can even take some example to the real-world communication.

After all, we are talking here about how to communicate better in general, and communication is the key to any relationship.

Feb 25 2022
SoftwareBackend

So you got a task at work to build a REST API for your customers, that will be comfortable. But where do you start? Don’t worry, you’re in the right place!

First thing’s first: What’s an API, anyway?

We hear this term A LOT, usually in contexts of HTTP API. API is the acronym for Application Programming Interface, and that is the language that allows two applications to talk to each other.

For example, If I am an application that provides TV info, My API will describe the ways of communicating with me. in this case:

  • You can ask me: Hey how’s the show “Gilmore Girls”?
  • And I’ll answer: The show is: good/bad. (Of course, the accurate answer here is “best show everrr!11”)

HTTP API

This API above is described in English, but it’s 2022 and most applications communicate between them in HTTP, which is a protocol for fetching resources from remote servers (such as HTML documents). Don’t panic! the resource is just something we provide to the one who requests it. For example, we use it to communicate between a browser to a server, between services in a Kubernetes cluster, etc etc.

If we would describe our API in the HTTP language, we could expose the following API request:

GET how-is-the-show/TOO_HOT_TO_HANDLE

And we could describe the answer to this request as

200 {result: "SO Bad"}

This means the request has been processed successfully, and the show is bad. or, if the show is “peaky-blinders-season-9”, we will get a -

404 {error: "Show not found"}

This tutorial assumes you already are familiar with HTTP, but if you need a refreshment, A short tutorial to some HTTP methods could be found here https://assertible.com/blog/7-http-methods-every-web-developer-should-know-and-how-to-test-them.

What’s a customer-facing API?

Every API has a vendor and a consumer. If it’s an internal microservice’s API, the consumer may be another microservice. if it’s a website API, the consumer may be the frontend code that calls these requests and process them to render on the browser screen. But in various cases, the consumer of the API will be customers outside of your company. for example

  • Google Auth API: the vendor is Google and the consumers are a client who wants to use the “Login through Google” option.
  • AWS CLI / Boto3, the vendor is AWS and the consumers of the APIs are DevOps or developers who wish to manage their cloud resources.

In general, building an API has multiple technical aspects such as what is the technology we use to build the layer that exposes the API, authentication, and authorization, how do we manage different versions of the API, for backward compatibility, how do we manage the documentation. You can fill books with those subjects.

However, today we will solely focus on the API, We are forming a language here, and it’s pretty cool. so let’s go!

Know your enemy customer

Designing a customer-facing API is special. First, because those who use our API are people from outside your organization, that need to understand our language. They’re not your co-workers who have to deal with your internal API anyway and can ask you for clarifications. In addition, unlike writing a user interface, our users are mostly technical, like us, the developers, so we are talking the same language. however we must think about intuitivism for them, they need to understand what we do without us teaching them, if possible.

So before we start, let’s understand who exactly are our customers are and what do they need from us.

  • How technical our users are? Can they write code, or just send HTTP requests using Postman? All of these will affect how complex and rich our API can be.
  • What are the actions the users are going to perform? do they only read data, or also mutate the persisted data?
  • What amount of flexibility is needed? Do we need to provide an answer to a constant question, or do we need to answer several variations of the same questions, with different parameters? In our shows example, do we just need to provide a good/bad? maybe an option to provide the shows I loved before that?

Summary

We learned what the term API really means, and identified the differences between internal API and customers API. In the next chapter, we will learn about practical methods and approaches for implementation.

December 11, 2021
Personal Story
My (possibly) amusing anecdotes on how the programming world looks from a child's eye, in Israel of 2005, together with some original-retro-Photoshop craft by me, and, what should you do when you meet a child with an extraordinary passion for technology?
Back in 2005, I started my way in programming with studying some basic Web technologies. you know, HTML, CSS, JS, the usuals. This sounds about right, since I am now a software engineer. Only, I was 11 years old back then.


It's funny to see that besides my favorite color, not a lot has changed (It's turquise now, by the way)

CD, The early days

Oh, these were funny days, where my CI/CD process included manually uploading bunch of files to a hosting site, and version rollout included putting a "under construction" sign until I'm done with the process.


You're probably wondering how I got here

I'm not sure what led me to this point where I study programming all by myself when other kids played with dolls. None of my family worked in the high-tech industry. I was just a child who liked very much to create new stuff, to draw, to paint, to build. and I guess I figured out at some point that programming IS some sort of creation itself.

Sababa

My sharp grandfather, however, is a surprisingly technological man. when other elderly were afraid of the internet, he surfed there freely. In my website I used to have a "Guest book", where people can leave me messages. There was one secret fan of mine, that was identified as "Sababa" (Sababa is the equivalent of "cool/alright!"), always left nice messages about how cool the website is. It took me years to figure out is was him all along ("Saba ba" also means "grandpa's here"!)

Magic

I didn't know any English by then, and didn't have someone to tutor me. So I sticked to the one website that had Hebrew materials, and went over the tutorials there. One of the tutorials was for XML. I remember there were examples for stuff like <Apple />, <Circle color="red" />, and I was sure it was some kind of a magic language, where you could write anything, open in a browser and it will come true! well, just like the letter from Hogwarts never came, so did this

First name dropping ever

It's funny about CSS; I wasn't familiar with this concept in the beginning. I remember wanting to have my website in 4 different colors, and the user is able to choose in what color to see the website in. The rational thing to do in my opinion was to duplicate the whole website 4 times, each time with different color tags (you remember the html color in tags? <body bgcolor/>, <bgsound/>) . There was one kid I knew that knew how to code. I once told him about my new feature, all excited. He rolled his eyes, and said, "But why aren't you just using CSS?" oh, the shame.


When I think about it now, I don't regret this years spent in front of the computer as a kid, it definitely helped me later to be accepted to the excellent 8200 unit in the military and from there to Microsoft etc. However, I do wonder: what would have happen if I had a mentor, a tutor, someone to guide me at this time? where could have I reach?

So my message to you is - a. of course expose children you know to this wonderful platform, that is coding. and b. more importantly, if you identify around you a kid who is a crazy enthusiast about something, anything, programming, dinosaurs, dancing, you name it. Encourage him or she, keep it fun for them (please don't ruin it with university classes.. ) and try to provide him the best tools to carry on. Who knows, it might pay off for them later on in their lives.
Nov 2021
Productivity
Or: how can you become a better developer via understanding if you are a bfs-person or a dfs-person
In Computer Science, Breadth-first search (BFS) and Depth-first search (DFS) are algorithms for searching/scanning a tree data structure for a specific node. they both start with the tree root and eventually covering all nodes in the tree, the difference is in how they scan the nodes, in what order.

BFS explores all nodes by depths levels (first all nodes at depth 1, then depth 2, and so on) until the required node was found. DFS explore all nodes at a specific route to a leaf. when this route is exahausted, we move to the next route to a leaf (backtracking).


At some point I've found out that these two concepts of searching, also matching 2 approaches of developers when they get a development task.

One persona (DFS) starts a development task. in the middle, he (or she) notices he doesn't like the convention they are using for variables in the codebase. on the way of changing that, he notices another potential bug and fixes it too, on the way of fixing this bug, he finds out that some functions are not well-documented, and decides to document them, and so on and so forth.

The BFS one, on the other hand, is getting a task, and doing like a "medical surgery" to change the exact place that needs to be fixed and nothing more, doing the minimal effort possible that should be done on order to mark the task as "Done" and go home.

What persona you think describes you the most? (My answer is in the end)

How do you think it affects your work, in terms of: reputation, code-quality, fast-delivery?

Which approach you think is better?

In life, like in life, things aren't black and white. each of us is sometimes this and sometimes that, and none of the approach is generally better than the other, but, each of them matching different situations. BFS-coding makes you deliver much faster, therefore important on urgent tasks, on start-ups where things have to get done quickly and the hell with the infrastructure. DFS-coding makes you work very thoroughly, which can be used in infrastructural tasks, like general refactoring in the code. And it also kinda makes you look like a superman to your team, that's also nice and good for your reputation.

BTW, I'm totally tend to be a BFS-person myself, but I think I've found a good compromise. If during the development I see something that bothers me, I write it down in my notes. eventually I get to all of these "side-jobs", but in the right time for me, and without delaying urgent tasks.

In conclusion, I hope you understood the different between being a BFS-person and a DFS person. your awareness to this subject alone, will help you better navigate between your tasks, and hopefully make you a better developer or a developer's team lead.

Have a nice coding!